package org.ljy.lotterysystem.service.impl;

import lombok.extern.slf4j.Slf4j;
import org.ljy.lotterysystem.common.errorcode.ServiceErrorCodeConstants;
import org.ljy.lotterysystem.common.exception.ServiceException;
import org.ljy.lotterysystem.common.utils.JacksonUtil;
import org.ljy.lotterysystem.common.utils.RedisUtil;
import org.ljy.lotterysystem.controller.param.CreateActivityParam;
import org.ljy.lotterysystem.controller.param.CreatePrizeByActivityParam;
import org.ljy.lotterysystem.controller.param.CreateUserByActivityParam;
import org.ljy.lotterysystem.controller.param.PageParam;
import org.ljy.lotterysystem.dao.dataobject.*;
import org.ljy.lotterysystem.dao.mapper.*;
import org.ljy.lotterysystem.service.IActivityService;
import org.ljy.lotterysystem.service.dto.ActivityDTO;
import org.ljy.lotterysystem.service.dto.ActivityDetailDTO;
import org.ljy.lotterysystem.service.dto.CreateActivityDTO;
import org.ljy.lotterysystem.service.dto.PageListDTO;
import org.ljy.lotterysystem.service.enums.ActivityPrizeStatusEnum;
import org.ljy.lotterysystem.service.enums.ActivityPrizeTiersEnum;
import org.ljy.lotterysystem.service.enums.ActivityStatusEnum;
import org.ljy.lotterysystem.service.enums.ActivityUserStatusEnum;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;

import java.util.List;
import java.util.Optional;
import java.util.stream.Collectors;

@Slf4j
@Service
public class ActivityServiceImpl implements IActivityService {


    @Autowired
    private UserMapper userMapper;

    @Autowired
    private PrizeMapper prizeMapper;

    @Autowired
    private ActivityMapper activityMapper;

    @Autowired
    private ActivityPrizeMapper activityPrizeMapper;

    @Autowired
    private ActivityUserMapper activityUserMapper;

    @Autowired
    private RedisUtil redisUtil;

    /**
     * redis：存储活动信息的前缀
     */
    private final String ACTIVITY_PREFIX = "ACTIVITY_";
    /**
     * redis：活动缓存过期时间
     */
    private final Long ACTIVITY_TIMEOUT = 60 * 60 * 24 * 3L;


    /**
     * 1.创建活动
     * @param param
     * @return
     */
    @Override
    @Transactional(rollbackFor = Exception.class) //使用事务保障
    public CreateActivityDTO createActivity(CreateActivityParam param) {
        //1.校验参数
        checkActivityInfo(param);
        //2.保存活动信息
        ActivityDO activityDO = new ActivityDO();
        activityDO.setActivityName(param.getActivityName());
        activityDO.setDescription(param.getDescription());
        activityDO.setStatus(ActivityStatusEnum.RUNNING.name());
        activityMapper.insert(activityDO);
        //3.保存活动关联的奖品信息
        List<CreatePrizeByActivityParam> prizeParams = param.getActivityPrizeList();//先从总参数中获取活动关联奖品列表
        //转成DO类型
        List<ActivityPrizeDO> activityPrizeDOList = prizeParams.stream()
                .map(prizeParam -> {
                    ActivityPrizeDO activityPrizeDO = new ActivityPrizeDO();
                    activityPrizeDO.setActivityId(activityDO.getId());
                    activityPrizeDO.setPrizeId(prizeParam.getPrizeId());
                    activityPrizeDO.setPrizeAmount(prizeParam.getPrizeAmount());
                    activityPrizeDO.setPrizeTiers(prizeParam.getPrizeTiers());
                    activityPrizeDO.setStatus(ActivityPrizeStatusEnum.INIT.name());
                    return activityPrizeDO;
                }).collect(Collectors.toList());
        activityPrizeMapper.batchInsert(activityPrizeDOList);
        //4.保存活动关联的人员信息
        List<CreateUserByActivityParam> userParams = param.getActivityUserList(); //获取人员列表
        //转成成DO
        List<ActivityUserDO> activityUserDOList = userParams.stream()
                .map(userParam -> {
                    ActivityUserDO activityUserDO = new ActivityUserDO();
                    activityUserDO.setActivityId(activityDO.getId());
                    activityUserDO.setUserId(userParam.getUserId());
                    activityUserDO.setUserName(userParam.getUserName());
                    activityUserDO.setStatus(ActivityUserStatusEnum.INIT.name());
                    return activityUserDO;
                }).collect(Collectors.toList());
        activityUserMapper.batchInsert(activityUserDOList);
        //5.整合完整的活动信息，用来存放到redis中
        //新建一个对象ActivityDetailDTO:活动信息+人员信息+奖品信息（需要两个对象--奖品+活动关联奖品）
        List<Long> prizeIds = param.getActivityPrizeList().stream()
                .map(CreatePrizeByActivityParam::getPrizeId)
                .collect(Collectors.toList()); //获取所有奖品id
        List<PrizeDO> prizeDOList = prizeMapper.batchSelectByIds(prizeIds);//根据奖品id查询所有的奖品信息
        //5.1.整合对象
        ActivityDetailDTO activityDetailDTO = convertToActivityDetailDTO(activityDO,activityUserDOList,
                                                    prizeDOList,activityPrizeDOList);
        //5.2.存入redis
        cacheActivity(activityDetailDTO);
        //6.构造结果返回
        CreateActivityDTO activityDTO = new CreateActivityDTO();
        activityDTO.setActivityId(activityDO.getId());
        return activityDTO;
    }

    /**
     * 2.获取活动列表
     * @param param
     * @return
     */
    @Override
    public PageListDTO<ActivityDTO> findActivityList(PageParam param) {
        //1.获取总数
        int total = activityMapper.count();
        //2.获取活动列表
        List<ActivityDO> activityDOList = activityMapper.findActivityList(param.getCurrentPage(),param.getPageSize());
        //3.转化活动列表
        List<ActivityDTO> activityDTOList = activityDOList
                .stream()
                .map(
                        ActivityDO -> {
                            ActivityDTO activityDTO = new ActivityDTO();
                            activityDTO.setActivityId(ActivityDO.getId());
                            activityDTO.setActivityName(ActivityDO.getActivityName());
                            activityDTO.setDescription(ActivityDO.getDescription());
                            activityDTO.setStatus(ActivityStatusEnum.forName(ActivityDO.getStatus()));
                            return activityDTO;
                        }
                )
                .collect(Collectors.toList());
        //4.构造返回
        return new PageListDTO<>(total, activityDTOList);
    }

    /**
     * 3.获取完整的活动信息
     * @param activityId
     * @return
     */
    @Override
    public ActivityDetailDTO getActivityDetail(Long activityId) {
        //1.
        if(activityId == null) {
            log.error("activityId is null");
            throw new ServiceException(ServiceErrorCodeConstants.ACTIVITY_ID_ERROR);
        }
        //2.从redis中获取
        ActivityDetailDTO activityDetailDTO = getActivityFromCache(activityId);
        if(activityDetailDTO != null) {
            log.error("value:{}", JacksonUtil.writeValueAsString(activityDetailDTO));
            return activityDetailDTO;
        }
        //3.缓存中不存在，从mysql中获取
        //活动表
        ActivityDO aDO = activityMapper.selectByActivityId(activityId);
        //活动奖品表
        List<ActivityPrizeDO> apDOList = activityPrizeMapper.selectByActivityId(activityId);
        //活动人员表
        List<ActivityUserDO> auDOList = activityUserMapper.selectByActivityId(activityId);
        //奖品表
        List<Long> prizeIds = apDOList.stream()
                .map(ActivityPrizeDO::getPrizeId)
                .collect(Collectors.toList());
        List<PrizeDO> pDO = prizeMapper.batchSelectByIds(prizeIds);
        //整合,存入redis中（调用现成的方法即可）
        activityDetailDTO = convertToActivityDetailDTO(aDO,auDOList,pDO,apDOList);
        cacheActivity(activityDetailDTO);
        //返回
        return activityDetailDTO;
    }

    @Override
    public void cacheActivity(Long activityId) {
        if(activityId == null) {
            log.warn("activityId is null");
            throw new ServiceException(ServiceErrorCodeConstants.ACTIVITY_ERROR);
        }

        //活动表
        ActivityDO aDO = activityMapper.selectByActivityId(activityId);
        if(aDO == null) {
            log.error("activityId is null,{cacheActivity}");
            throw new ServiceException(ServiceErrorCodeConstants.ACTIVITY_ERROR);
        }
        //活动奖品表
        List<ActivityPrizeDO> apDOList = activityPrizeMapper.selectByActivityId(activityId);
        //活动人员表
        List<ActivityUserDO> auDOList = activityUserMapper.selectByActivityId(activityId);
        //奖品表id
        List<Long> prizeIds = apDOList.stream()
                .map(ActivityPrizeDO::getPrizeId)
                .collect(Collectors.toList());
        List<PrizeDO> pDO = prizeMapper.batchSelectByIds(prizeIds);
        //整合,存入redis中（调用现成的方法即可）
        ActivityDetailDTO activityDetailDTO = convertToActivityDetailDTO(aDO,auDOList,pDO,apDOList);
        cacheActivity(activityDetailDTO);
    }

    /**
     * 1.5缓存活动信息
     * @param detailDTO
     */
    private void cacheActivity(ActivityDetailDTO detailDTO) {
        // key: ACTIVITY_12
        //1. value: ActivityDetailDTO(json)
        if (null == detailDTO || null == detailDTO.getActivityId()) {
            log.warn("要缓存的活动信息不存在!");
            return;
        }
        //2.
        try {
            redisUtil.set(ACTIVITY_PREFIX+detailDTO.getActivityId(),//前缀+活动id
                    JacksonUtil.writeValueAsString(detailDTO),//转成json的value
                    ACTIVITY_TIMEOUT);//过期时间
        }catch (Exception e) {
            log.error("缓存失败,activityId:{}",detailDTO.getActivityId());
        }
    }

    /**
     * 1.5从缓存中获取活动信息
     * @return
     */
    private ActivityDetailDTO getActivityFromCache(Long activityId) {
        if(activityId == null) {
            log.warn("获取缓存活动数据的activityId为空！");
            return null;
        }
        try {
            String value = redisUtil.get(ACTIVITY_PREFIX+activityId);
            if(!StringUtils.hasText(value)) {
                //System.out.println(JacksonUtil.writeValueAsString(value));
                log.info("获取的缓存活动数据为空！key={}", ACTIVITY_PREFIX + activityId);
                return null;
            }
            return JacksonUtil.readValue(value, ActivityDetailDTO.class);
        }catch (Exception e) {
            log.error("从缓存中获取活动信息异常，key={}", ACTIVITY_PREFIX + activityId, e);
            return null;
        }
    }


    /**
     *  1.4将活动信息+人员+奖品信息整合在一定
     * @param activityDO 活动数据
     * @param activityUserDOList 活动关联人员数据
     * @param prizeDOList 奖品数据
     * @param activityPrizeDOList 活动关联奖品数据
     * @return 整合后的对象
     */
    private ActivityDetailDTO convertToActivityDetailDTO(ActivityDO activityDO,
                                                         List<ActivityUserDO> activityUserDOList,
                                                         List<PrizeDO> prizeDOList,
                                                         List<ActivityPrizeDO> activityPrizeDOList) {
        ActivityDetailDTO activityDetailDTO = new ActivityDetailDTO();
        //1.存入活动信息数据
        activityDetailDTO.setActivityId(activityDO.getId());
        activityDetailDTO.setActivityName(activityDO.getActivityName());
        activityDetailDTO.setDesc(activityDO.getDescription());
        activityDetailDTO.setStatus(ActivityStatusEnum.forName(activityDO.getStatus()));
        //2.存入奖品相关信息
        List<ActivityDetailDTO.PrizeDTO> prizeDTOList = activityPrizeDOList.stream()
                .map(apDO -> {
                    ActivityDetailDTO.PrizeDTO prizeDTO = new ActivityDetailDTO.PrizeDTO();
                    //1.从activityPrizeDOList参数中获取
                    prizeDTO.setPrizeId(apDO.getPrizeId());
                    prizeDTO.setTiers(ActivityPrizeTiersEnum.forName(apDO.getPrizeTiers()));
                    prizeDTO.setPrizeAmount(apDO.getPrizeAmount());
                    prizeDTO.setStatus(ActivityPrizeStatusEnum.forName(apDO.getStatus()));
                    //2.从prizeDOList参数中获取
                    Optional<PrizeDO> optionalPrizeDO = prizeDOList.stream()
                            .filter(prizeDO -> prizeDO.getId().equals(apDO.getPrizeId()))
                            .findFirst();
                    // 如果PrizeDO为空，不执行当前方法，不为空才执行
                    optionalPrizeDO.ifPresent(prizeDO -> {
                        prizeDTO.setName(prizeDO.getName());
                        prizeDTO.setImageUrl(prizeDO.getImageUrl());
                        prizeDTO.setPrice(prizeDO.getPrice());
                        prizeDTO.setDescription(prizeDO.getDescription());
                    });
                    return prizeDTO;
                }).collect(Collectors.toList());
        //
        activityDetailDTO.setPrizeDTOList(prizeDTOList);//塞入奖品信息
        //3.存入人员相关信息
        List<ActivityDetailDTO.UserDTO> userDTOList = activityUserDOList.stream()
                .map(auDO -> {
                    ActivityDetailDTO.UserDTO userDTO = new ActivityDetailDTO.UserDTO();
                    userDTO.setUserId(auDO.getUserId());
                    userDTO.setUserName(auDO.getUserName());
                    userDTO.setStatus(ActivityUserStatusEnum.forName(auDO.getStatus()));
                    return userDTO;
                })
                .collect(Collectors.toList());
        //
        activityDetailDTO.setUserDTOList(userDTOList);
        return activityDetailDTO;
    }

    /**
     * 1.1.校验活创建活动的参数信息
     * @param param
     */
    private void checkActivityInfo(CreateActivityParam param) {
        if(param == null) {
            log.warn("ActivityServiceImpl, createActivity, param is null");
            throw new ServiceException(ServiceErrorCodeConstants.ACTIVITY_CREATE_ERROR);
        }
        //1.检查-人员id在人员列表中是否都存在
        //1.1获取人员列表中的所有人员id
        List<Long> userIds =
                param.getActivityUserList()
                        .stream()
                        .map(CreateUserByActivityParam::getUserId)
                        .distinct()
                        .collect(Collectors.toList());
        //1.2.调用dao,返回一个存在于dao的id集
        List<Long> existUserIds = userMapper.selectExistByIds(userIds);
        if(CollectionUtils.isEmpty(existUserIds)) {
            log.warn("ActivityServiceImpl, createActivity, userIds is empty");
            throw new ServiceException(ServiceErrorCodeConstants.ACTIVITY_USER_ERROR);
        }
        //1.3.检查是否一一对应
        userIds.forEach(id -> {
            if (!existUserIds.contains(id)) {
                throw new ServiceException(ServiceErrorCodeConstants.ACTIVITY_USER_ERROR);
            }
        });
        //2.检查-奖品id在奖品表中是否都存在
        //2.1.获取奖品id集
        List<Long> prizeIds =
                param.getActivityPrizeList()
                        .stream()
                        .map(CreatePrizeByActivityParam::getPrizeId)
                        .distinct()
                        .collect(Collectors.toList());
        List<Long> existPrizeIds = prizeMapper.selectExistByIds(prizeIds);
        if(CollectionUtils.isEmpty(existPrizeIds)) {
            log.warn("ActivityServiceImpl, createActivity, userIds is empty");
            throw new ServiceException(ServiceErrorCodeConstants.ACTIVITY_PRIZE_ERROR);
        }
        //2.3.检查是否一一对应
        prizeIds.forEach(id -> {
            if (!existPrizeIds.contains(id)) {
                log.warn("ActivityServiceImpl, createActivity, prizeIds is error");
                throw new ServiceException(ServiceErrorCodeConstants.ACTIVITY_PRIZE_ERROR);
            }
        });

        //3.人员数量是否 >= 奖品数量
        long userAmount = param.getActivityUserList().size();
        long prizeAmount = param.getActivityPrizeList()
                .stream()
                .mapToLong(CreatePrizeByActivityParam::getPrizeAmount)//对每一个奖品数量求和
                .sum();
        if(userAmount < prizeAmount) {
            log.warn("ActivityServiceImpl, createActivity, userIds is greater than prizeAmount");
            throw new ServiceException(ServiceErrorCodeConstants.USER_PRIZE_AMOUNT_ERROR);
        }
        //4.校验活动奖品等奖有效性
        param.getActivityPrizeList().forEach(prize -> {
            if(null == ActivityPrizeTiersEnum.forName(prize.getPrizeTiers())) {
                throw new ServiceException(ServiceErrorCodeConstants.ACTIVITY_PRIZE_TIERS_ERROR);
            }
        });
    }

}
